// 用proxy 监听对象后,数据的获取会出发get函数
// obj.name = xxx 收集依赖

// 用map收集所有依赖
// {
// 	target:{
// 		key: [effect1,effect2,...]
// 	}
// }

// 全局的map
const targetMap = new WeakMap()
let effectStack = [] // 存储effect

// 收集
function track(target,key){
	// 初始化 得到最后一个元素
	const effect = effectStack[effectStack.length - 1]
	if(effect){
		// 需要收集 
		let depMap = targetMap.get(target)
		if(depMap === undefined){
			depMap = new Map()
			targetMap.set(target,depMap)
		}
		
		let dep = depMap.get(key)
		if(dep === undefined){
			dep = new Set()
			depMap.set(key,dep)
		}
		// 初始化完成  开始收集依赖 双向缓存
		if(!dep.has(effect)){
			dep.add(effect) // 存储 把effect放在dep里面
			effect.deps.push(dep) // 双向缓存
		}
	}
}

// 执行
function trigger(target,key,info){
	let depMap = targetMap.get(target)
	console.log(depMap,'depMap')
	
	if(depMap === undefined){
		return // 没有effect 【副作用】
	}
	
	const effects = new Set()
	const computeds = new Set() // computed是一个特殊的effect
	
	if(key){
		let deps = depMap.get(key)
		console.log('deps-trigger==>',deps)
		// 遍历 effect 找到对应的 effect
		deps.forEach(effect => {
			if(effect.computed){
				computeds.add(effect)
			}else{
				effects.add(effect)
			}
		})
	}
	effects.forEach(effect => effect())
	computeds.forEach(computed => computed())
}

const handleCallback = {
	// 还有删除 是否存在
	get(target,key){
		const res = target[key]  // 实际用 Reflect.get(target,key))
		// 收集依赖 到全局的map
		track(target,key)
		return res  // 递归 typeof res == 'object' ? reactive(res) : res
	},
	set(target,key,val){
		const info = {
			oldVla:target[key],
			newVal: val
		}
		target[key] = val // Reflect.set
		// 拿到收集的 effect 执行下
		trigger(target,key,info) // 执行effect
	}
}

function reactive(target){
	// Object.defineProperty()  ---> vue2
	const observed = new Proxy(target,handleCallback)
	return observed
}

function computed(fn){
	const runner = effect(fn,{computed:true, lazy:true})
	return {
		effect: runner,
		get value(){
			return runner()
		}
	}
}

function effect(fn,options={}){
	let cb = createReactiveEffect(fn,options)
	if(!options.lazy){
		cb()
	}
	return cb
}

function createReactiveEffect(fn,options){
	const effect =function effect(...args){
		return run(effect, fn, args)
	}
	effect.deps = []
	effect.computed = options.computed
	effect.lazy = options.lazy
	return effect
}

// 调度
function run(effect, fn, args){
	if(effectStack.indexOf(effect) === -1){
		try{
			effectStack.push(effect)
			return fn(...args)
		}finally{
			effectStack.pop()
		}
	}
}